<?php

namespace app\models\tableModel;

use app\entity\PageListEntity;
use app\models\tableModel\base\AdminMenu;
use app\service\tool\GenerateService;
use app\service\tool\UtilsService;
use yii\helpers\ArrayHelper;

/**
 * 后台菜单[Model]
 * User: Administrator
 * Date: 1995/12/22
 * Time: 17:59
 */
class AdminMenuModel extends AdminMenu
{

    /**
     * 状态 列表
     * @var array
     */
    private static $statusList = [
        [
            'key'   => 'disabled',
            'value' => -1,
            'text'  => '禁用',
        ],
        [
            'key'   => 'open',
            'value' => 1,
            'text'  => '开启',
        ]
    ];

    /**
     * 是否有子集 列表
     * @var array
     */
    private static $hasChildList = [
        [
            'key'   => 'has',
            'value' => 1,
            'text'  => '有子集',
        ],
        [
            'key'   => 'not',
            'value' => 0,
            'text'  => '无子集',
        ]
    ];
    /**
     * 排序最大值
     * @var int
     */
    protected static $sortMax = 999999;
    /**
     * 排序最小值
     * @var int
     */
    protected static $sortMin = -999999;

    /**
     * id等级列表
     * @var array
     */
    private $lvIdList = [];

    /**
     * 父级专用条件
     * @var array
     */
    private $parentWhere = [];
    /**
     * 子级专用条件
     * @var array
     */
    private $childWhere = [];

    /**
     * 规则验证
     * @return array
     */
    public function rules()
    {

        $parent = parent::rules();
        // 状态
        $statusList = array_column(self::getStatList(), 'value');
        // 是否有下级
        $hasChildList = array_column(self::getHasChildList(), 'value');

        return ArrayHelper::merge($parent, [
            ['status', 'in', 'range' => $statusList, 'message' => '状态不合法'],
            ['has_child', 'in', 'range' => $hasChildList, 'message' => '是否存在下级不合法'],
            ['parent_id', 'checkParentId'],
            ['top_id', 'checkTopId'],
        ]);
    }

    /**
     * 验证[parent_id]是否合法
     * @param null $attributeNames
     * @param bool $clearErrors
     * @return bool
     */
    public function checkParentId($attributeNames = null, $clearErrors = true)
    {

        // [0]组委父级菜单直接成功
        if ($this->$attributeNames == 0) {
            return true;
        }

        $parentItem = self::findOne([
            'id' => $this->$attributeNames
        ]);
        // 记录不存在
        if (empty($parentItem)) {
            $this->addError($attributeNames, '父级菜单不存在');
            return false;
        }
        // 记录不存在
        if ($parentItem['status'] == self::getStatDisabled()) {
            $this->addError($attributeNames, '父级菜单已禁用');
            return false;
        }

        // 赋值找到的顶部编号
        $this->top_id = $parentItem['top_id'];

        // 如果父级编号是空的 || 此次操作的父级顶级编号和查询到的编号相同
        if (empty($parentItem['parent_id']) || $this->top_id == $parentItem['parent_id']) {
            return true;
        }

        // 否则。在向上一级找到顶级
        $parentItem = self::findOne([
            'id' => $parentItem['parent_id']
        ]);

        // 顶级记录不存在
        if (empty($parentItem)) {
            $this->addError($attributeNames, '顶级菜单不存在');
            return false;
        }
        // 顶级记录不存在
        if ($parentItem['status'] == self::getStatDisabled()) {
            $this->addError($attributeNames, '顶级菜单已禁用');
            return false;
        }

        return true;
    }

    /**
     * 验证[top_id]是否合法
     * @param null $attributeNames
     * @param bool $clearErrors
     * @return bool
     */
    public function checkTopId($attributeNames = null, $clearErrors = true)
    {

        // [0]组委父级菜单直接成功
        if ($this->$attributeNames == 0) {
            return true;
        }

        $parentItem = self::findOne([
            'id' => $this->$attributeNames
        ]);
        // 记录不存在
        if (empty($parentItem)) {
            $this->addError($attributeNames, '顶级菜单不存在');
            return false;
        }
        // 记录不存在
        if ($parentItem['status'] == self::getStatDisabled()) {
            $this->addError($attributeNames, '顶级菜单已禁用');
            return false;
        }

        return true;
    }

    /**
     * 重写label的 文字
     */
    public function attributeLabels()
    {
        return [
            'id'     => '编号',
            'name'   => '名称',
            'status' => '状态',
        ];
    }

    /**
     * 场景
     * @return array
     */
    public function scenarios()
    {

        $scenarios = parent::scenarios();
        return ArrayHelper::merge($scenarios, [
            [
                // 自定义场景 (无用请删除)
                'scUpdate' => [
                    'someAttributes'
                ]
            ]
        ]);
    }

    /**
     * 自定义过滤单条[where]
     *  ` 仅限于通过方法[loadWhere]传入的条件, 可以在其中自定义逻辑
     *  ` 保证最终返回值为一维数组[Yii2]通用条件格式[运算符, 字段名, 值]
     * @param $condition array [sql]查询条件, 严格遵循Yii多条件查询:[运算符, 字段名, 值]
     * @return array
     */
    public function whereCustomFilter($condition = [])
    {

        // // 例：字段为编号 —— 操作符使用[IN]
        // if ($field == 'id') {
        //     return ['IN', $condition[1], $value[2]];
        // }

        // 最终返回必须为数组：[运算符, 字段名, 值]，符合Yii2规范
        return $condition;
    }

    /**
     * 获取全部列表
     * @param null $field 字段
     * @param array $opt 其他设置, 详情参考[formatData]方法的变量
     * @return PageListEntity
     */
    public function getPaginate($field = null, $opt = [])
    {

        // 查找的 字段空的 就默认给列表
        if (!$field) $field = '*';

        // 基础[where]加载完毕
        $this->getBaseSql()->select($field);

        // 需要查询父级编号的
        if (isset($opt['parentId'])) {
            $this->getBaseSql()->where(
                UtilsService::whereRm($this->where, 'parent_id')
            )->andWhere([
                'AND',
                ['=', 'parent_id', $opt['parentId']]
            ]);
        }

        // 数据的获取 分页等
        $list = $this->getBaseSql()->orderBy("`sort` DESC, `id` ASC")->asArray()->all();
        // 格式化数据后的列表
        $list = $this->formatData($list, $field, $opt);
        // 总数量
        $total = $this->getBaseSql()->count();

        return PageListEntity::loadModel(compact('list', 'total'));
    }

    /**
     * 最后一级编号获取等级编号列表
     * @param $id
     * @return array
     */
    public function lastGetLvIdList($id = false)
    {

        if (!$id) {
            return [0 => 0];
        }

        $detail = self::findOne(['id' => $id]);
        // 数据不存在返回
        if (empty($detail)) return [];

        // 父级是空的则最后一次了
        if (empty($detail['parent_id'])) return [$detail['id']];

        $parentIdList = $this->lastGetLvIdList($detail['parent_id']);

        // id是自身的话直接返回列表
        if ($id == $this->id) return $parentIdList;

        return array_merge($parentIdList, [$id]);
    }

    /**
     * 格式化列表活详情数据
     * @param array $list 列表
     * @param array $opt 其他设置
     * @param array $fields 字段列表
     *  ` delNullFolder: bool 是否删除空子集文件夹
     *  ` idIsKey: 编号作为键
     *  ` loadLevelIds: bool 是否加载等级编号列表
     *  ` parentId - array 加载某条目列表
     *  ` loadChild - bool 是否加载子集
     * @return mixed
     */
    public function formatData($list, $fields = [], $opt = [])
    {

        // 为空直接返回
        if (empty($list)) return $list;
        // 需要返回第一组（可能不是二维数组）
        $needFirst = false;
        if (!is_array(array_values($list)[0])) {
            $needFirst = true;
            $list      = [$list];
        }

        ### 一些设置
        // 是否是否删除空子集文件夹
        $delNullFolder = isset($opt['delNullFolder']) && boolval($opt['delNullFolder']) !== false;
        // 编号为键值
        $idIsKey = isset($opt['idIsKey']) && boolval($opt['idIsKey']) !== false;
        // 是否加载等级编号列表
        $loadLevelIds = isset($opt['loadLevelIds']) && boolval($opt['loadLevelIds']) !== false;
        // 是否加载子集
        $loadChild = isset($opt['loadChild']) && boolval($opt['loadChild']) !== false;

        // 格式化数据
        foreach ($list as $k => &$v) {

            // 状态文本
            if (isset($v['status'])) {
                $v['status_text'] = self::getStatusText($v['status']);
            }

            // 是否禁用
            if (isset($v['status']) && $v['status'] == self::getStatDisabled()) {
                $v['disabled'] = true;
            }

            // [id]赋值[value]
            if (isset($v['id'])) {
                $v['value'] = $v['id'];
                $v['key']   = $v['id'];
            }

            // [name]赋值[label]
            if (isset($v['name'])) {
                $v['label'] = $v['name'];
            }

            // 有子集获取子集
            if ($loadChild && isset($v['has_child']) && $v['has_child'] == self::getHasChildHas()) {

                $v['children'] = $this->getPaginate($fields, array_merge($opt, [
                    'parentId' => $v['id']
                ]));
                $v['children'] = $v['children']->getList();
            } else if ($loadChild) {
                $v['children'] = [];
            }

            // 加载子集列表
            if ($loadLevelIds) {
                // 子集等级列表
                $v['id_level_list'] = array_merge(
                    [$v['id']],
                    UtilsService::arrayListMerge(
                        array_column($v['children'], 'id_level_list')
                    )
                );
            }

            // 取出子集
            if ($loadChild && empty($v['children'])) unset($v['children']);

            // 如果是文件夹 没有子集删除
            if ($delNullFolder && $v['url'] == '#' && empty($v['children'])) {
                unset($list[$k]);
            }
        }

        // 编号是否作为键值
        if ($idIsKey) $list = array_column($list, null, 'id');

        reset($list);
        return $needFirst ? current($list) : $list;
    }

    /**
     * 添加|保存
     * @param bool $runValidation 是否仅仅运行验证，不运行保存
     * @param string[]|null|string $attributeNames 仅仅保存某几个字段
     * @return bool
     */
    public function save($runValidation = false, $attributeNames = null)
    {

        $nowTime = time();
        // 添加的话要赋值一些初始数据
        if (empty($this->id)) {

            // 可以是走mongoId
            $this->id       = GenerateService::newMongoId();
            $this->add_time = $nowTime;
        }
        // 父级编号默认赋值空则为[0]
        if (empty($this->parent_id)) {
            $this->parent_id = '0';
        }

        $this->update_time = $nowTime;

        // 检测
        if ($this->hasErrors() || !$this->validate($attributeNames)) {

            // 记录下错误日志
            \Yii::error([
                "`````````````````````````````````````````````````````````",
                "``                      数据库错误                       ``",
                "`` 错误详情: [[后台菜单]数据库操作模型]验证数据失败             ``",
                "`` 错误信息和参数详情:                                     ``",
                "`````````````````````````````````````````````````````````",
                $this->getAttributes(),
                $this->getErrors()
            ], 'db');
            return false;
        }

        // 需要 && 执行保存
        if (!$runValidation && !parent::save(false, $attributeNames)) {

            // 记录下错误日志
            \Yii::error([
                "`````````````````````````````````````````````````````````",
                "``                      数据库错误                       ``",
                "`` 错误详情: [[后台菜单]数据库操作模型]保存数据失败             ``",
                "`` 错误信息和参数详情:                                     ``",
                "`````````````````````````````````````````````````````````",
                $this->getAttributes(),
                $this->getErrors()
            ], 'db');
            return false;
        }

        return true;
    }

    /**
     * 根据[Auth权限]过滤菜单列表
     * @param array $urlList
     * @param bool $isSuper
     * @return array
     */
    public function filterMenuByAuth($urlList = [], $isSuper = false)
    {

        // 权限是空的
        if (empty($urlList) && !$isSuper) return [];

        // 基础条件
        $baseWhere = [
            'AND'
        ];
        // 非超管 需查询连接
        if (!$isSuper) {
            $urlList[]   = '#'; // 父级列表
            $baseWhere[] = ['IN', 'url', $urlList];
        }

        $authUrlList[] = '#'; // 添加一个#父级列表
        $selfModel     = self::loadModel();
        $PageListEntity   = $selfModel->loadWhere(array_merge($baseWhere, [
            ['=', 'status', self::getStatOpen()], // 状态太正常的
            ['IN', 'parent_id', 0]
        ]))->getPaginate('id, name, url, font_icon, has_child, top_id', [
            'idIsKey'       => true, // 编号为键
            'delNullFolder' => true, // 删除空文件夹
            'loadChild'     => true, // 加载子集
        ]);
        return $PageListEntity->getList();
    }

    /**
     * 重新加载是否有子集
     *  ` 作用：将查询规定编号下是否有子集，也可以强制直接定义是否有或者无 `
     * @param $id
     * @param int $hasChild
     * @return bool
     */
    public static function updateHasChildren($id, $hasChild = null)
    {

        // [id]是数组直接返回否
        if (is_array($id)) return false;
        // 验证下是否有子集合法
        $hasChildList = array_values(self::getHasChildList());
        if (!in_array($hasChild, $hasChildList) || !$hasChild) {
            $childList = self::findAll(['parent_id' => $id]);
            $hasChild  = !$childList ? self::getHasChildNot() : self::getHasChildHas();
        }

        return self::updateField(['id' => $id], ['has_child' => $hasChild]);
    }

    /**
     * 更新某些字段
     * @param $condition
     * @param array $fieldVal
     * @return bool
     */
    public static function updateField($condition, $fieldVal = [])
    {

        // 验证字段
        $model = self::loadModel();
        $model->load($fieldVal, '');
        if (!$model->validate(array_keys($fieldVal))) {
            $error                          = UtilsService::getModelError($model->getErrors());
            self::$error_[$error['column']] = [$error['msg']];
            return false;
        }
        // 重新取值规则化后的特定值列表
        $fieldVal = $model->getAttributes(array_keys($fieldVal));

        $db = \Yii::$app->db->createCommand();

        try {

            $db->update(self::tableName(), $fieldVal, $condition)->execute();

            // 否则成功
            return true;
        } catch (\Exception $error) {

            // 记录下错误日志
            \Yii::error([

                "````````````````````````````````````````````````````````",
                "``                      数据库错误                      ``",
                "`` 错误详情: 修改[指定字段]失败，{$error->getMessage()}      ``",
                "`` SQL语句: {$db->getRawSql()}                         ``",
                "`` 错误信息和参数详情:                                    ``",
                "````````````````````````````````````````````````````````",
                $error->getTraceAsString()
            ], 'db');

            // 静态错误
            self::$error_['db_error'] = empty(self::$error_['db_error']) ?
                [$error->getMessage()] : array_merge(self::$error_['db_error'], [$error->getMessage()]);

            return false;
        }
    }


    /**
     * 返回排序最大值
     * @return int
     */
    public static function getSortMax()
    {
        return self::$sortMax;
    }

    /**
     * 返回排序最小值
     * @return int
     */
    public static function getSortMin()
    {
        return self::$sortMin;
    }

    /**
     * 获取[正常]状态 值
     * @return mixed|string
     */
    public static function getStatOpen()
    {

        $list = array_column(self::$statusList, null, 'key');
        return $list['open']['value'];
    }

    /**
     * 获取[禁用]状态 值
     * @return mixed|string
     */
    public static function getStatDisabled()
    {

        $list = array_column(self::$statusList, null, 'key');
        return $list['disabled']['value'];
    }

    /**
     * 获取[状态]列表 值
     * @return mixed|string
     */
    public static function getStatList()
    {

        // 最终正常返回
        return self::$statusList;
    }

    /**
     * 获取[状态]文本
     * @param $value
     * @return mixed|string
     */
    public static function getStatusText($value)
    {

        // 列表
        $list = array_column(self::$statusList, null, 'value');
        // 不合法 - 不存在
        if (empty($list[$value]['text']))

            return '--';

        // 最终正常返回
        return $list[$value]['text'];
    }


    /**
     * 获取静态错误
     * @return mixed
     */
    public static function getStaticErrors()
    {
        return self::$error_;
    }

    /**
     * 获取[是否有子集]有 值
     * @return mixed
     */
    public static function getHasChildHas()
    {

        $list = array_column(self::$hasChildList, null, 'key');
        return $list['has']['value'];
    }

    /**
     * 获取[是否有子集]无 值
     * @return mixed
     */
    public static function getHasChildNot()
    {

        $list = array_column(self::$hasChildList, null, 'key');
        return $list['not']['value'];
    }

    /**
     * 获取[是否有子集]列表 值
     * @return array
     */
    public static function getHasChildList()
    {
        // 最终正常返回
        return self::$hasChildList;
    }
}
